//
//  MPBModelSupport.m
//  基本工具类的封装
//  MPBModelType：封装运行时属性的类型
//  MPBModelProperty：运行时属性的封装
//
//

#import "MPBModelSupport.h"


/// 全局属性编码参数
/// Int 类型
NSString *const LKTypeInt = @"i";

/// float 类型
NSString *const LKTypeFloat = @"f";

/// double 类型
NSString *const LKTypeDouble = @"d";

/// long 类型
NSString *const LKTypeLong = @"q";

/// long long 类型
NSString *const LKTypeLongLong = @"q";

/// char 类型
NSString *const LKTypeChar = @"c";

/// bool 类型（用不到）
NSString *const LKTypeBOOL = @"B";

/// pointer 类型
NSString *const LKTypePointer = @"*";

/// ivar 类型
NSString *const LKTypeIvar = @"^{objc_ivar=}";

/// method 类型
NSString *const LKTypeMethod = @"^{objc_method=}";

/// block 类型
NSString *const LKTypeBlock = @"@?";

/// class 类型
NSString *const LKTypeClass = @"#";

/// SEL 类型
NSString *const LKTypeSEL = @":";

/// id 类型
NSString *const LKTypeId = @"@";


/// 定义一个全局集合
static NSSet *_foundationClasses;


/*!
 *
 *  @brief  支持类
 *

 */
@implementation MPBModelSupport


/*!
 *
 *  @brief  在类加载时，初始化全局集合
 *

 */
+ (void)load
{
    _foundationClasses = [NSSet setWithObjects:
                          [NSObject class],
                          [NSURL class],
                          [NSDate class],
                          [NSNumber class],
                          [NSDecimalNumber class],
                          [NSData class],
                          [NSMutableData class],
                          [NSArray class],
                          [NSMutableArray class],
                          [NSDictionary class],
                          [NSMutableDictionary class],
                          [NSString class],
                          [NSMutableString class],
                          nil];
}

/*!
 *  
 *  @brief  判断是否为框架类型
 *
 *  @param  c 需要判断的类型
 *
 *  @return 是否
 *

 */
+ (BOOL)isClassFromFoundation:(Class)c
{
    return [_foundationClasses containsObject:c];
}

@end


/*!
 *
 *  @brief  运行时属性类型
 *  @  1、保存每个运行时属性的定义类型
 *  @  2、能够实现属性名称更改
 *  @  3、能够实现赋值类型的安全性校验
 *
 */
@implementation MPBModelType

/// 全局缓存
static NSMutableDictionary *_cachedTypes;

/*!
 *
 *  @brief  类第一次加载时初始化全局缓存
 *

 */
+ (void)load
{
    _cachedTypes = [NSMutableDictionary dictionary];
}

/*!
 *
 *  @brief  类方法，创建 type 对象
 *
 *  @param  code 属性编码 @encode
 *
 *  @return type 对象
 *

 */
+ (instancetype)cachedTypeWithCode:(NSString *)code
{
    NSAssert(code != nil, @"code 不能为空");
    
    /// 使用全局字典，缓存 属性编码，提升性能
    MPBModelType *type = _cachedTypes[code];
    
    /// 没有就创建
    if (type == nil)
    {
        type = [[self alloc] init];
        type.code = code;
        
        /// 保存到全局缓存
        _cachedTypes[code] = type;
    }
    
    return type;
}

/*!
 *
 *  @brief  通过属性编码判断属性的定义类型
 *
 *  @param  code 属性编码 encode
 *
 */
- (void)setCode:(NSString *)code
{
    _code = code;
    
    /// 根据属性编码进行类型判断
    if ([code isEqualToString:LKTypeId])
    {
        _idType = YES;
//        return;
    }
    else if (code.length == 0)
    {
        _KVCDisabled = YES;
//        return;
    }
    else if (code.length > 3 && [code hasPrefix:@"@\""])
    {
        /// 去掉 @" 和 "，截取中间的类型名称
        _code = [code substringWithRange:NSMakeRange(2, code.length - 3)];
        
        /// 基本类型不会进入，typeClass 属性默认为 nil
        _typeClass = NSClassFromString(_code);
        
        /// 是否为系统内置类型
        _fromFoundation = [MPBModelSupport isClassFromFoundation:_typeClass];
        
        /// 此处使用两次判断，通过逻辑运算符的特性提升性能
        _numberType = (_typeClass == [NSNumber class] ||
                       [_typeClass isSubclassOfClass:[NSNumber class]]);
        
//        return;
    }
    else if ([code isEqualToString:LKTypeSEL] ||
               [code isEqualToString:LKTypeIvar] ||
               [code isEqualToString:LKTypeMethod])
    {
        _KVCDisabled = YES;
        
//        return;
    }
    
    /// 是否为数字类型
    NSString *lowerCode = _code.lowercaseString;
    NSArray *numberTypes = @[LKTypeInt, LKTypeFloat, LKTypeDouble, LKTypeLong, LKTypeChar];
    if ([numberTypes containsObject:lowerCode])
    {
        _numberType = YES;
    }
}

@end


/*!
 *
 *  @  运行时属性的封装
 *  @  封装了属性的定义名称、字典 key、所在的类
 *

 */
@interface MPBModelProperty()

/// 类名作为 key，保存的属性 key 值
@property (strong, nonatomic) NSMutableDictionary *keyDict;

@end


/*!
 *
 *  @brief  封装的运行时属性类型
 *

 */
@implementation MPBModelProperty


/*!
 *
 *  @brief  懒加载
 *
 */
- (NSMutableDictionary *)keyDict
{
    if (!_keyDict)
    {
        self.keyDict = [NSMutableDictionary dictionary];
    }
    
    return _keyDict;
}

/*!
 *
 *  @brief  创建并缓存封装的运行时属性
 *
 *  @param  property 需要处理的运行时属性
 *
 *  @return 封装的运行时属性
 *

 */
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property
{
    /// 获取给类绑定的封装属性对象
    MPBModelProperty *propertyObj = objc_getAssociatedObject(self, property);
    
    if (propertyObj == nil ||
        ![propertyObj isKindOfClass:[MPBModelProperty class]])
    {
        propertyObj = [[self alloc] init];
        propertyObj.property = property;
        
        /// 给类绑定封装的运行时属性对象
        objc_setAssociatedObject(self,
                                 property,
                                 propertyObj, 
                                 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    return propertyObj;
}

/*!
 *
 *  @brief  监听运行时属性赋值，同时处理相关属性
 *
 *  @param  property 运行时属性
 *

 */
- (void)setProperty:(objc_property_t)property
{
    _property = property;
    
//    NSAssert(property != nil, @"property 不能为空");
    if (property == nil) return;
    
    /// 定义的属性名
    _name = @(property_getName(property));
    
    /// 成员类型
    NSString *attrs = @(property_getAttributes(property));
    NSUInteger loc = 1;
    NSUInteger len = [attrs rangeOfString:@","].location - loc;
    
    /// 处理属性的封装类型
    _type = [MPBModelType cachedTypeWithCode:[attrs substringWithRange:NSMakeRange(loc, len)]];
}

/*!
 *
 *  @brief  对应着字典中的 key
 *
 *  @param  key 字典中的 key
 *  @param  c   对应的类
 *

 */
- (void)setKey:(NSString *)key forClass:(Class)c
{
    if (!key) return;
    self.keyDict[NSStringFromClass(c)] = key;
}

/*!
 *
 *  @brief  类中存放的字典 key
 *
 *  @param  c 对应的类
 *
 *  @return 字典中的 key
 *

 */
- (NSString *)keyFromClass:(Class)c
{
    return self.keyDict[NSStringFromClass(c)];
}

/*!
 *
 *  @brief  设置成员变量的值
 *
 *  @param  value  值
 *  @param  object 赋值的对象
 *

 */
- (void)setValue:(id)value forObject:(id)object
{
    /// 当服务器返回的数组属性没有值时，需要给数组赋值 nil
    if (_type.KVCDisabled || !value || value == [NSNull null])
    {
        return;
    }
    /// 给对应的属性赋值
    [object setValue:value forKey:_name];
}

@end
